home *** CD-ROM | disk | FTP | other *** search
/ The Business Master (3rd Edition) / The Business Master (3rd Edition).iso / files / utilstic / showmcb / watch.asm < prev    next >
Encoding:
Assembly Source File  |  1992-07-05  |  24.5 KB  |  648 lines

  1. ;WATCH.ASM - Modified for ShowMCBs 5.00 by Tom Gilbert's Heart & Mind
  2. ;resident routine watches programs going resident and keeps a list of
  3. ;interrupt vector changes in an internal table of data structures
  4. ;======================================================================
  5. ; to be assembled by TASM
  6. ; Copyright (c) 1986,1991 Kim Kokkonen, TurboPower Software.
  7. ; May be freely distributed but not sold except by permission.
  8. ; telephone: 719-260-6641, Compuserve 76004,2611
  9. ;======================================================================
  10. ; version 2.2  3/4/87
  11. ;   First release, version to be consistent with MAPMEM.PAS
  12. ; :
  13. ; long intervening history
  14. ; :
  15. ; version 3.0  9/24/91
  16. ;   add tracking for TSRs that unload themselves
  17. ;   add support for TSRs loaded high
  18. ;   WATCH may be loaded high
  19. ; version 3.1  11/4/91
  20. ;   rewrite again to solve problems with SWAPMM, FSP, DATAPATH, DATAMON
  21. ; version 3.2  11/22/91
  22. ;   change method of accessing high memory
  23. ;   deal with DOS 5 MODE int trapping (int seg < psp seg)
  24. ; version 3.3  1/8/92
  25. ;   relocate AddChain code so that it doesn't get overwritten if there
  26. ;     are lots of initial memory blocks
  27. ; version 3.4  2/14/92
  28. ;   no change
  29. ; version 3.5  7/5/92
  30. ;   cosmetic changes like these line lengths and
  31. ;   terminating "TSR WATCHER" command line with CR
  32. ;   nulled out storage area and put "SS"s in Stack
  33. ;   this makes RamView(ing) less confusing and the
  34. ;   initialization messages are different on 1 line.
  35. ;======================================================================
  36. ;uncomment following line to generate more publics in MAP file
  37. ;       debug   = 1
  38. cseg    segment public para
  39.         assume  cs:cseg, ds:nothing, es:nothing, ss:nothing
  40.         locals  @@
  41.         org     080H
  42. cmdline label   byte                    ;pointer to command line
  43.         org     100H
  44. pentry: jmp     init
  45. ;always put the following in WATCH.MAP to update MEMU.PAS
  46. public nextchange,emesg,changevectors,origvectors
  47. ;**********************************************************************
  48. ;data structures part of COM file
  49.                 even
  50. nextchange      dw      0               ;next position to write
  51.                                         ;in changes area
  52. firstmcb        dw      ?               ;first MCB segment
  53. firsthimcb      dw      0               ;first high memory MCB segment
  54. ;temporary stack used by interrupt handler
  55. newsp           dw      ?               ;initial stack pointer
  56. newss           dw      ?               ;segment of our temporary stack
  57. tmpret          dw      ?               ;used while switching stacks
  58.  
  59. ;information saved about the calling program
  60. oldsp           dw      ?               ;stack pointer
  61. oldss           dw      ?               ;stack segment
  62. ;previous interrupt handlers
  63. dos_int         label dword
  64. old21           dw 2 dup (?)            ;old int21 vector
  65. tsr_int         label dword
  66. old27           dw 2 dup (?)            ;old int27 vector
  67. ;XMS access
  68. xmsadr  label   dword                   ;XMS control address
  69. xmsxxx  dw      2 dup (0)
  70. ;id code for a PSP data block
  71. pspid           equ     0FFFFH          ;id used to start a PSP block
  72. ;structure of a changevectors data block
  73. pspblock        struc
  74.                 id      dw      ?       ;id word, always pspid
  75.                 psp     dw      ?       ;psp segment
  76.                 len     dw      ?       ;length of psp
  77.                 unu1    dw      ?       ;unused
  78. pspblock        ends
  79. vecblock        struc
  80.                 vec     dw      ?       ;vector number 0..255
  81.                 veco    dw      ?       ;vector offset
  82.                 vecs    dw      ?       ;vector segment
  83.                 unu2    dw      ?       ;unused
  84. vecblock        ends
  85. ;**********************************************************************
  86. ;resident data structures not part of COM file
  87. changevectors   =   offset emesg            ;data area overwrites emesg
  88. vrecsize        =   8                       ;bytes/vector change record
  89. maxchanges      =   128                     ;maximum vector changes
  90. vsize           =   maxchanges*vrecsize     ;size of vector change area
  91. ;vector table buffers
  92. origvectors     =   offset changevectors+vsize ;original vector table
  93. veclen          =   1024                    ;size of vector table
  94. newstackpos     =   origvectors+veclen      ;location of newstack
  95. ssize           =   128                     ;bytes in temporary stack
  96. newloc          =   newstackpos+ssize       ;stack top and end of TSR
  97. ;**********************************************************************
  98. ;int21 handler
  99. ;  traps functions 31, 49, 4C, and 7761
  100. int21h  proc far
  101. ifdef   debug
  102.         public  int21h
  103. endif
  104.         assume ds:nothing
  105.         pushf                           ;save flags
  106.         sti                             ;allow interrupts
  107.         cmp     ah,31H                  ;TSR call?
  108.         jne     @@1
  109.         call    addcurrpsp              ;dx = paras to keep
  110.         jmp     short @@4
  111.  
  112. @@1:    cmp     ah,49H                  ;deallocate block call?
  113.         jne     @@2
  114.         call    remblock                ;remove specified block
  115.         jmp     short @@4               ;if a psp
  116.  
  117. @@2:    cmp     ah,4CH                  ;normal program halt?
  118.         jne     @@3
  119.         call    checkblocks
  120.         jmp     short @@4
  121. @@3:    cmp     ax,7761H                ;"wa"tch ID call?
  122.         jne     @@4
  123.         call    checkblocks             ;assure change list up to date
  124.         push    bp
  125.         mov     bp,sp                   ;set up stack frame
  126.         and     word ptr [bp+8],0FFFEH  ;clear carry flag
  127.         pop     bp
  128.         xchg    ah,al                   ;flip ah and al as a signature
  129.         mov     bx,cs                   ;return WATCH psp in bx
  130.         popf
  131.         iret                            ;return to caller
  132. @@4:    popf
  133.         jmp     dos_int                 ;let DOS take over
  134. int21h  endp
  135. ;**********************************************************************
  136. ;int27 handler
  137. ;  watches for programs going resident
  138. int27h  proc far
  139. ifdef   debug
  140.         public int27h
  141. endif
  142.         assume ds:nothing
  143.         pushf
  144.         sti
  145.         push   dx
  146.         add    dx,15            ;pass size of block in paras
  147.         shr    dx,1
  148.         shr    dx,1
  149.         shr    dx,1
  150.         shr    dx,1
  151.         call   addcurrpsp       ;get current psp and add block to list
  152.         pop    dx
  153.         popf
  154.         jmp     tsr_int
  155. int27h  endp
  156. ;**********************************************************************
  157. ;get current PSP in bx and add new block
  158. ;entry: dx = paragraphs to keep
  159. addcurrpsp proc near
  160. ifdef   debug
  161.         public  addcurrpsp
  162. endif
  163.         assume  ds:nothing
  164.         call    setup           ;switch stacks and save registers
  165.         assume  ds:cseg
  166.         mov     ah,51H          ;get current PSP in bx
  167.         pushf
  168.         call    dos_int
  169.         call    addblock        ;add block at bx, length dx to changes
  170.         call    shutdown        ;restore registers and switch stacks
  171.         assume  ds:nothing
  172.         ret
  173. addcurrpsp endp
  174. ;***********************************************************************
  175. ;remove PSP block, if any, specified by es
  176. remblock proc near
  177. ifdef   debug
  178.         public  remblock
  179. endif
  180.         assume  ds:nothing
  181.         call    setup           ;switch stacks and save registers
  182.         assume  ds:cseg
  183.         mov     bx,es           ;save segment being deallocated in bx
  184.         call    matchpsp        ;return offset from changevectors
  185.         or      si,si           ;any matching block?
  186.         jz      @@1
  187.         call    rempsp          ;remove psp
  188. @@1:    call    shutdown        ;restore registers and switch stacks
  189.         assume  ds:nothing
  190.         ret
  191. remblock endp
  192. ;**********************************************************************
  193. ;scan chain of mcbs starting at segment ax
  194. ;remove halting psp from change list if needed
  195. checkchain proc near
  196. ifdef   debug
  197.         public  checkchain
  198. endif
  199. @@1:    mov     es,ax
  200.         mov     bx,es:[0001h]           ;bx = psp of block
  201.         mov     dx,es:[0003h]           ;dx = len of block
  202.         inc     ax
  203.         cmp     ax,bx                   ;does psp = mcb+1?
  204.         jne     @@2                     ;jump if not
  205.         cmp     ax,cx                   ;does psp = current program?
  206.         je      @@2                     ;jump if so
  207.         push    dx
  208.         call    matchpsp                ;find changevectors psp match
  209.         pop     dx
  210.         or      si,si                   ;is there a matching psp?
  211.         jnz     @@2                     ;jump if so
  212.         push    ax
  213.         push    cx
  214.         push    dx
  215.         call    addblock                ;add this psp
  216.         pop     dx
  217.         pop     cx
  218.         pop     ax
  219. @@2:    cmp     byte ptr es:[0000h],'Z' ;end of chain
  220.         je      @@3
  221.         add     ax,dx
  222.         jmp     @@1
  223. @@3:    ret
  224. checkchain endp
  225. ;**********************************************************************
  226. ;check for new memory blocks and add if needed
  227. ;remove halting psp from change list if needed
  228. checkblocks proc near
  229. ifdef   debug
  230.         public  checkblocks
  231. endif
  232.         assume  ds:nothing
  233.         call    setup                   ;switch stacks & save registers
  234.         assume  ds:cseg
  235.         mov     ah,51H                  ;get current psp in bx
  236.         pushf
  237.         call    dos_int
  238.         call    matchpsp                ;is current program
  239.         or      si,si                   ;in change list?
  240.         jz      @@0                     ;jump if not
  241.         call    rempsp                  ;remove it if not
  242. @@0:    mov     cx,bx                   ;cx = psp of halting program
  243.         mov     ax,firstmcb             ;start with first mcb
  244.         call    checkchain              ;check this chain
  245.         mov     ax,firsthimcb           ;scan high memory
  246.         or      ax,ax
  247.         jz      @@1
  248.         call    checkchain
  249. @@1:    call    shutdown                ;restore regs & switch stacks
  250.         assume  ds:nothing
  251.         ret
  252. checkblocks endp
  253. ;**********************************************************************
  254. ;setup routine for interrupt hook routines
  255. ; switches stacks, saves registers, sets ds=cs
  256. setup   proc    near
  257. ifdef   debug
  258.         public  setup
  259. endif
  260.         assume  ds:nothing
  261.         pop     cs:tmpret       ;save return address as switch stacks
  262.         mov     oldss,ss        ;save current stack
  263.         mov     oldsp,sp
  264.         cli                     ;switch to our stack
  265.         mov     ss,newss
  266.         mov     sp,newsp
  267.         sti
  268.         push    ax              ;store registers
  269.         push    bx
  270.         push    cx
  271.         push    dx
  272.         push    si
  273.         push    di
  274.         push    bp
  275.         push    ds
  276.         push    es
  277.         push    cs              ;set ds=cs
  278.         pop     ds
  279.         assume  ds:cseg
  280.         push    cs:tmpret       ;return
  281.         ret
  282. setup   endp
  283.  
  284. ;**********************************************************************
  285. ;shutdown routine for interrupt hook routines
  286. ; restores registers, switches stacks
  287. shutdown proc near
  288. ifdef   debug
  289.         public  shutdown
  290. endif
  291.         pop     cs:tmpret
  292.         pop     es              ;restore registers
  293.         pop     ds
  294.         assume ds:nothing
  295.         pop     bp
  296.         pop     di
  297.         pop     si
  298.         pop     dx
  299.         pop     cx
  300.         pop     bx
  301.         pop     ax
  302.         cli                     ;restore stack
  303.         mov     ss,cs:oldss
  304.         mov     sp,cs:oldsp
  305.         sti
  306.         push    cs:tmpret       ;return
  307.         ret
  308. shutdown endp
  309. ;**********************************************************************
  310. ;add specified block to changes
  311. ;  entry: bx = psp of block, dx = length of block in paras
  312. addblock proc near
  313. ifdef   debug
  314.         public  addblock
  315. endif
  316.         assume  ds:cseg
  317.         call    addhdr          ;add a psp header block
  318.         call    addvecs         ;add blocks for each hooked vector
  319.         ret
  320. addblock endp
  321. ;**********************************************************************
  322. ;add header for a psp block
  323. ;  entry: bx = psp of block, dx = length of block in paras
  324. ;  exit:  alters di
  325. addhdr  proc near
  326. ifdef   debug
  327.         public  addhdr
  328. endif
  329.         assume  ds:nothing
  330.         mov     di,nextchange
  331.         cmp     di,vsize-vrecsize       ;assure room for next record
  332.         ja      @@1
  333.         mov     word ptr cs:changevectors[di].id,pspid
  334.         mov     cs:changevectors[di].psp,bx
  335.         mov     cs:changevectors[di].len,dx
  336.         add     di,vrecsize
  337.         mov     nextchange,di
  338. @@1:    ret
  339. addhdr  endp
  340.  
  341. ;**********************************************************************
  342. ;add vector blocks for each hooked vector
  343. ;  entry: bx = psp of block, dx = length of block in paras
  344. ;  exit:  alters ax,cx,dx,si,di,bp
  345. addvecs proc    near
  346. ifdef   debug
  347.         public  addvecs
  348. endif
  349.         assume  ds:cseg
  350.         push    ds
  351.         add     dx,bx                   ;now dx points to end of block
  352.         mov     di,nextchange           ;cs:changevectors[di] -> blocks
  353.         xor     si,si
  354.         mov     ds,si                   ;ds:si -> vectors
  355.         assume  ds:nothing
  356.         xor     cx,cx                   ;cx = vector counter
  357.         cld                             ;forward
  358. @@1:    lodsw                           ;ax = vector offset
  359.         mov     bp,ax                   ;save vector offset
  360.         lodsw                           ;ax = vector segment
  361.         cmp     ax,dx                   ;is vector above high limit?
  362.         jae     @@3
  363.         push    cx
  364.         mov     cx,ax
  365.         cmp     bp,800h                 ;don't add unless offset small
  366.         ja      @@1a                    ;(this is trap for DOS 5 MODE)
  367.         push    bp
  368.         shr     bp,1
  369.         shr     bp,1
  370.         shr     bp,1
  371.         shr     bp,1
  372.         add     cx,bp                   ;ax = equivalent segment of int
  373.         pop     bp
  374. @@1a:   cmp     cx,bx                   ;is vector above low limit?
  375.         jb      @@2
  376.         pop     cx
  377.         cmp     di,vsize-vrecsize       ;room for another entry?
  378.         ja      @@3
  379.         mov     cs:changevectors[di].vec,cx ;save entry for this vector
  380.         mov     cs:changevectors[di].veco,bp
  381.         mov     cs:changevectors[di].vecs,ax
  382.         add     di,vrecsize
  383.         jmp     short @@3
  384. @@2:    pop     cx
  385. @@3:    inc     cx                      ;next vector
  386.         cmp     cx,0FFh
  387.         jbe     @@1
  388.         mov     nextchange,di
  389.         pop     ds
  390.         assume  ds:cseg
  391.         ret
  392. addvecs endp
  393.  
  394. ;**********************************************************************
  395. ;find changeblock matching psp
  396. ;  entry: bx = psp to match
  397. ;  exit: si = matching block, or 0 if none
  398. ;        destroys dx
  399. matchpsp proc near
  400. ifdef   debug
  401.         public  matchpsp
  402. endif
  403.         assume  ds:cseg
  404.         mov     si,offset changevectors
  405.         mov     dx,si
  406.         add     dx,nextchange           ;dx = next unused changevector
  407. @@1:    cmp     si,dx                   ;end of table
  408.         jae     @@3
  409.         cmp     word ptr [si].id,pspid  ;psp indicator?
  410.         jnz     @@2                     ;jump if not
  411.         cmp     [si].psp,bx             ;matching psp?
  412.         jnz     @@2                     ;jump if not
  413.         ret                             ;else return with match
  414. @@2:    add     si,vrecsize
  415.         jmp     @@1
  416. @@3:    xor     si,si                   ;no match if here
  417.         ret
  418. matchpsp endp
  419. ;**********************************************************************
  420. ;remove all blocks associated with psp at offset si
  421. ;  exit: alters cx,dx,si,di,es
  422. rempsp  proc near
  423. ifdef   debug
  424.         public  rempsp
  425. endif
  426.         assume  ds:cseg
  427.         mov     di,si                   ;save destination
  428.         add     si,vrecsize             ;move to next record
  429.         mov     dx,offset changevectors
  430.         add     dx,nextchange           ;dx = address of next unused
  431. @@1:    cmp     si,dx                   ;end of table?
  432.         jae     @@2                     ;jump if so
  433.         cmp     word ptr [si].id,pspid  ;next psp indicator?
  434.         je      @@2                     ;jump if so
  435.         add     si,vrecsize             ;next block
  436.         jmp     @@1                     ;and loop
  437. @@2:    mov     cx,dx
  438.         sub     cx,si
  439.         shr     cx,1                    ;cx = words to move
  440.         push    cs
  441.         pop     es                      ;es = ds = cs
  442.         cld
  443.         rep     movsw                   ;copy down remaining blocks
  444.         sub     si,di
  445.         sub     nextchange,si           ;update nextchange
  446.         ret
  447. rempsp  endp
  448. align 16
  449. ;**********************************************************************
  450. ;resident portion above, temporary portion below
  451. ;**********************************************************************
  452. emesg   db      'already '
  453. imesg   db      'installed.',13,10,36
  454. mesg    db      'WATCH 3.5, '
  455. pname   db      'TSR WATCHER'
  456. plen    equ     $-pname                 ;length of command line
  457.         db      ', Copyright 1992 TurboPower Software, $'
  458.  
  459. ;**********************************************************************
  460. init    proc    near
  461. ifdef   debug
  462.         public  init
  463. endif
  464.         assume  ds:cseg
  465.         mov     dx,offset mesg          ;success message
  466.         mov     ah,09H
  467.         int     21H                     ;DOS print string
  468. ;use int 21h test to check for previous installation
  469.         mov     ax,7761H                ;special id function
  470.         int     21H                     ;if function fails
  471.         jc      @@1                     ;not installed
  472.         cmp     ax,6177H                ;if id code not returned
  473.         jnz     @@1                     ;not installed
  474. ;error exit
  475.         mov    dx,offset emesg          ;error message
  476.         mov    ah,09H
  477.         int    21H                      ;DOS print string
  478.         mov    ax,4C01H                 ;exit with error
  479.         int    21H
  480. ;not already installed
  481. @@1:    mov    dx,offset imesg          ;success message
  482.         mov    ah,09H
  483.         int    21H                      ;DOS print string
  484. ;initialize location of WATCH stack
  485.         mov     newsp,newstackpos+ssize
  486.         mov     newss,cs                ;stack seg is code seg
  487. ;put id label at offset 80H to allow other programs to recognize WATCH
  488.         mov     cx,plen                 ;length of name string
  489.         mov     si,offset pname         ;offset of name string
  490.         mov     di,offset cmdline       ;offset of DOS command line
  491.         cld                             ;transfer in forward direction
  492.         mov     al,cl
  493.         stosb                           ;store length byte first
  494.         rep     movsb                   ;transfer characters
  495.         mov     byte ptr es:[di],13     ;terminate command line
  496. ;relocate ourselves out of the way of the resident tables
  497.         push    cs
  498.         pop     es
  499.         mov     di,newloc+10H
  500.         push    di                      ;will act as a return address
  501.         mov     si,offset @@2
  502.         mov     cx,endcode-@@2
  503.         rep     movsb                   ;move code
  504.         ret                             ;"return" to the relocated code
  505. @@2:
  506. ;store image of original vector table (overwrites messages & used code)
  507.         push    cs
  508.         pop     es
  509.         xor     ax,ax                   ;nul-out
  510.         mov     di,offset emesg         ;storage
  511.         mov     cx,400h                 ;area
  512.         rep     stosw                   ;words
  513.         mov     ax,"SS"                 ;Initialize
  514.         mov     cx,64                   ;Stack Words
  515.         rep     stosw
  516.         mov     di,origvectors
  517.         push    ds
  518.         xor     si,si                   ;offset 0
  519.         mov     ds,si                   ;source address segment 0
  520.         mov     cx,200H                 ;512 words to store
  521.         rep     movsw                   ;copy vectors to our table
  522.         pop     ds
  523. ;add psp records for all blocks already resident
  524.         call    adddummypsp
  525. ;store current int 21 and 27 vectors
  526.         mov     ax,3527H
  527.         int     21H
  528.         mov     old27,bx
  529.         mov     old27[2],es
  530.         mov     ax,3521H
  531.         int     21H
  532.         mov     old21,bx
  533.         mov     old21[2],es
  534. ;install new vectors
  535.         mov    ax,2527H
  536.         mov    dx,offset int27h
  537.         int    21H
  538.         mov    ax,2521H
  539.         mov    dx,offset int21h
  540.         int    21H
  541. ;terminate and stay resident
  542.         mov    dx,newloc
  543.         add    dx,15
  544.         mov    cl,4
  545.         shr    dx,cl
  546.         mov    ax,3100H         ;return success code
  547.         int    21H              ;note WATCH will track itself
  548. @@3:
  549. init    endp
  550. ;**********************************************************************
  551. ;add dummy changeblocks for psps resident in chain starting at ax
  552. addchain proc near
  553. ifdef   debug
  554.         public  addchain
  555. endif
  556.         assume  ds:cseg
  557. @@1:    mov     es,ax
  558.         mov     bx,es:[0001h]           ;bx = psp of block
  559.         mov     dx,es:[0003h]           ;dx = len of block
  560.         inc     ax
  561.         cmp     ax,bx                   ;does psp = mcb+1?
  562.         jne     @@2                     ;jump if not
  563.         cmp     ax,newss                ;does psp = WATCH itself?
  564.         je      @@2                     ;jump if so
  565.         mov     cx,offset addhdr
  566.         call    cx
  567. ;       call    addhdr                  ;add a header for this block
  568. @@2:    cmp     byte ptr es:[0000h],'Z' ;end of chain
  569.         je      @@3
  570.         add     ax,dx
  571.         jmp     @@1
  572. @@3:    ret
  573. addchain endp
  574.  
  575. ;**********************************************************************
  576. ;return segment of first high memory mcb in ax
  577. findhimemstart proc near
  578. ifdef   debug
  579.         public findhimemstart
  580. endif
  581.         mov     ax,3000h                ;get DOS version
  582.         int     21H
  583.         cmp     al,3
  584.         jb      @@7                     ;no XMS driver possible
  585.         mov     ax,4300h
  586.         int     2Fh                     ;multiplex call for XMS
  587.         cmp     al,80h                  ;proper signature?
  588.         jne     @@7                     ;no XMS driver
  589.         mov     ax,4310h
  590.         int     2Fh
  591.         mov     xmsxxx,bx               ;save XMS control address
  592.         mov     xmsxxx[2],es
  593.         mov     ah,10h
  594.         mov     dx,0FFFFh
  595.         call    xmsadr                  ;ask to allocate FFFF UMB paras
  596.         cmp     bl,0B0h                 ;if fail but UMBs available
  597.         je      @@0                     ;then get top of memory
  598.         cmp     bl,0B1h                 ;if UMBs all allocated
  599.         jne     @@7                     ;then no UMBs available
  600. @@0:    int     12H
  601.         mov     cl,6
  602.         shl     ax,cl                   ;get segment of top of memory
  603. @@1:    mov     es,ax
  604.         cmp     byte ptr es:[0000h],'M' ;potential mcb?
  605.         jnz     @@6                     ;not an mcb, try next segment
  606. @@2:    mov     cx,ax                   ;save potential start mcb in cx
  607. @@3:    inc     ax
  608.         add     ax,es:[0003h]           ;ax = start of next mcb
  609.         jc      @@5                     ;can't be an mcb if we wrapped
  610.         mov     es,ax                   ;address of next mcb
  611.         mov     dl,es:[0000h]
  612.         cmp     dl,'M'
  613.         jz      @@3                     ;good start mcb
  614.         cmp     dl,'Z'
  615.         jz      @@9                     ;good end mcb
  616. @@5:    mov     ax,cx                   ;restore last start segment
  617. @@6:    cmp     ax,0FFFFh               ;top of memory?
  618.         je      @@7
  619.         inc     ax                      ;try next segment
  620.         jmp     @@1
  621. @@7:    xor     cx,cx                   ;no matching UMB
  622. @@9:    mov     ax,cx                   ;return segment in ax
  623.         ret
  624. findhimemstart endp
  625. ;**********************************************************************
  626. ;add dummy changeblocks for all psps already resident
  627. adddummypsp proc near
  628. ifdef   debug
  629.         public  adddummypsp
  630. endif
  631.         assume  ds:cseg
  632.         mov     ah,52H
  633.         int     21H                     ;get DOS list of lists
  634.         mov     ax,es:[bx-2]            ;get first MCB segment
  635.         mov     firstmcb,ax             ;save it for use later too
  636.         call    addchain
  637.         call    findhimemstart          ;find first high memory mcb
  638.         mov     firsthimcb,ax           ;save it for use later too
  639.         or      ax,ax
  640.         jz      @@1
  641.         call    addchain                ;add blocks in high memory too
  642. @@1:    ret
  643. adddummypsp endp
  644.  
  645. endcode:
  646. cseg    ends
  647.         end     pentry
  648.